The COVID-19 outbreak is an unprecedented global public health challenge. It is a heated topic in social networks. People discuss it and share the news about it as the number of new cases are soaring. In order to analyse how people discuss it in Twiiter and what’s people’s response, I will use text analysis tools in this article.

My mission is to aquire the latest COVID-19 tweets data, clean the data for analysis with data transformation, then do modelling by text analysis methods such as Bag of Words, Sentiment Analysis, Association Rules and Topic Modelling.

Let’s start with extracting data from Twitter.

1. Data Obtaining

# load libraries
library(tidytext)
library(tm)
library(tidyverse)
library(textclean)
library(ggplot2)
library(ggthemes)
library(reshape2)
library(wordcloud)
library(topicmodels)
library(textdata)
library(rtweet)
# Extract data by function of rtweet
rt <- search_tweets(
  # Set search keyword
  q = "#COVID19", 
  
  # Set the number of tweets to return
  n = 18000, 
  
  # Specify the type of search results
  type = "mixed",
  
  # Do not include retweets
  include_rts = FALSE, 
  
  # Set tweet language to English
  lang = "en", 
  
  # Twitter rate limits cap the number of search results returned to 18,000 every 15 minutes. To request more than that, simply set retryonratelimit = TRUE and rtweet will wait for rate limit resets.
  retryonratelimit = TRUE
)
# save the returned data to csv
save_as_csv(rt, "covid19.csv")

After saving the tweets data, we can load it directly.

rt <- read.csv(file = "covid19.csv", stringsAsFactors = FALSE)
# check the dimension of returned tweets data
dim(rt)
[1] 17947    90

There are 17947 tweets returned to our dataframe with 90 variables.

# View the dataframe
View(rt)

Because we specified the search results by "mixed", so let’s order the tweets by post time and check the first and last 10 tweets.

# check the first 10 rows by ordering created_at time
head(rt[order(rt$created_at),], 10)
# check the last 10 rows by ordering created_at time
tail(rt[order(rt$created_at),], 10)

Only 3 tweets were posted at least 10 hours than others. So we can plot the number of tweets created by time to see the trend.

# Plot the frequency of tweets after 2020-03-28 23:00:00 with interval 1 min
ts_plot(rt %>% filter(created_at > "2020-03-28 23:00:00"), 
        by = "mins", 
        trim = 1, 
        color = "#00acee"
        ) +
        ggplot2::theme_minimal() +
        ggplot2::theme(plot.title = ggplot2::element_text(face = "bold")) +
        ggplot2::labs(
          x = NULL, y = NULL,
          title = "Frequency of #COVID19 tweets from past hours",
          subtitle = "Twitters counts aggregated using 1 mins intervals",
          caption = "\nSource: Data collected from Twitter's REST API via rtweet"
          )

We can see that the number of tweets created per minutes is in 50 to 150 range. At every o’clock and half past o’clock, the number of tweets published is much higher than other timing.

2. Data Transformation

We will use the text column of our rt dataset for text analysis so it is needed to transform the text column to corpus type.

# transform our data to source format
covid <- VectorSource(rt$text)

# check the first tweet
covid[1]
[1] "11 new cases of #COVID19 have been reported in Nigeria: 8 in Lagos, 2 in Enugu &amp; 1 in Edo State\n\nAs at 11:55pm 27th March, there are 81 confirmed cases of  #COVID19 reported in Nigeria. 3 have been discharged with 1 death. https://t.co/7p3v3qAcGM"
# transform the source file into a Corpus
cv_corpus <- VCorpus(covid)

# then inspect the first one tweet
inspect(cv_corpus[1])
<<VCorpus>>
Metadata:  corpus specific: 0, document level (indexed): 0
Content:  documents: 1

[[1]]
<<PlainTextDocument>>
Metadata:  7
Content:  chars: 250

3. Data Cleaning

Now text cleaning is to be conducted, as for text analysis it is necessary to remove punctuation, numbers, white space, stop words and specified words such as “virus”, “covid”, “coronavirus”. Therefor, we can create a function to clean our text data.

clean_corpus <- function(corp) {
  
  # remove any html format if any
  corp <- tm_map(corp, content_transformer(replace_html))
  
  # remove any URLs
  corp <- tm_map(corp, content_transformer(replace_url))  
  
  # transform all characters to lower case
  corp <- tm_map(corp, content_transformer(tolower))
  
  # word stemming
  corp <- tm_map(corp, stemDocument)  
  
  # remove punctuation
  corp <- tm_map(corp, removePunctuation)

  # remove numbers
  corp <- tm_map(corp, removeNumbers)  
  # strip extra whitespace
  corp <- tm_map(corp, stripWhitespace)
  
  # remove stop words and "apple"
  # Remove english common stopwords
  corp <- tm_map(corp, removeWords, stopwords("english"))
  corp <- tm_map(corp, removeWords, 
                 c("covid19", "coronavirus", "covid", "will", "can", "now", "get", "just",
                   "new", "peopl", "one", "virus", "case", "day", "time", "pendem", "like",
                   "take", "make", "say", "see"))
  
  # word stemming
  #corp <- tm_map(corp, stemDocument)
  
  # remove special characters
  #f <- content_transformer(function(x, pattern) gsub(pattern, "", x))
  #corp <- tm_map(corp, f, "\”“")
  # [\[\]"',.^&$%#]
  

  
  return(corp)
}
cv_corpus_cleaned <- clean_corpus(cv_corpus)
# print the first tweet after cleaning
cat("Before:\n", content(cv_corpus[[1]]), "\n\n")
Before:
 11 new cases of #COVID19 have been reported in Nigeria: 8 in Lagos, 2 in Enugu &amp; 1 in Edo State

As at 11:55pm 27th March, there are 81 confirmed cases of  #COVID19 reported in Nigeria. 3 have been discharged with 1 death. https://t.co/7p3v3qAcGM 
cat("After:\n", content(cv_corpus_cleaned[[1]]))
After:
        report  nigeria  lagos  enugu  edo state   pm th march   confirm    report  nigeria   discharg  death

Our function successfully changed all letters to lowercase, removed the numbers, url, punctuation, stop words and the specified words. In addition, the stemming was conducted for words like “reported”, “confirmed”, “discharged”. Now we can start our modelling work.

4. Modelling

In modelling part, we will apply four methods to understand the COVID-19 tweets data. 1. Bag of Words 2. Gauge Sentiment 3. Association Rules 4. Topic Modelling

4.1 Bag of Words Model

# transform the cleaned data into a doc-term matrix
dt_cv_corpus <- DocumentTermMatrix(cv_corpus_cleaned)

# print the dimension of the matrix
dim(dt_cv_corpus)
[1] 17947 38962

We have 17951 tweets and 38962 different words/terms.

# transform to matrix format
mt_cv_corpus <- as.matrix(dt_cv_corpus)
# view a portion of the matrix
mt_cv_corpus[1:10, 50:55]
    Terms
Docs ‘em ‘emerg ‘essenti ‘essential’ ‘everi ‘exit
  1     0       0         0             0       0      0
  2     0       0         0             0       0      0
  3     0       0         0             0       0      0
  4     0       0         0             0       0      0
  5     0       0         0             0       0      0
  6     0       0         0             0       0      0
  7     0       0         0             0       0      0
  8     0       0         0             0       0      0
  9     0       0         0             0       0      0
  10    0       0         0             0       0      0
# visualise twitter data
# convert from matrix to tidy data
tidy_data <- tidy(dt_cv_corpus)

tidy_data
term_fre <- colSums(mt_cv_corpus)
# sort term frequency in decreasing order
term_fre <- sort(term_fre, decreasing = TRUE)
freq_word <- data.frame(word = names(term_fre), freq = term_fre)

head(freq_word, 20)
# plot the most frequent 20 terms in our data
ggplot(head(freq_word, 20), aes(x = reorder(word, freq), y = freq), fill = "blue") + 
  geom_col(fill = "#00acee") +
  ggplot2::theme_minimal() +
  ggplot2::theme(plot.title = ggplot2::element_text(face = "bold")) +
  ggplot2::theme(axis.text.x = ggplot2::element_blank()) +
  ggplot2::labs(
    x = NULL, y = NULL,
    title = "Most frequent 20 words in #COVID19 tweets\n",
    caption = "\nSource: Data collected from Twitter's REST API via rtweet"
  ) +
  coord_flip() + geom_text(aes(label = freq), hjust = +1.2, color = "#FFFFFF", size = 3)

Having the frequency of each terms, let’s make a wordcloud to visualise the most frequent terms.

# create a vector of term frequency values
terms_vec <- names(term_fre)
# create wordcloud for 60 most frequent words
#library(viridisLite)
#color_pal <- plasma(n = 5)
wordcloud(words = terms_vec,
          freq = term_fre,
          min.freq = 300,
          max.words = 80,
          scale = c(3,0.13),
          random.order = FALSE, # more frequent words will be in center
          rot.per = 0.0, # proportion words with 90 degree rotation
          colors = brewer.pal(6, "Set2")) # Set color theme with 5 colors

The wordloud show the most frequent words in the middle. We can see words such as need, help, dure, test, death, stay, home. These words are definitely associated with coronavirus as we know the situation around the world. Many people need help and cure as they had taken test and increasing numbers of people were positive. Many people are durely suffering from the pandemic. The virus has lead to more than 10 thousand death. To protect ourselves, it is better to stay home and work from home.

Alternative way to visualise the wordcloud by wordcloud2 package. It creates a widget and generate the words from most frequent to less frequent.

library(wordcloud2)
wordcloud2(data = head(freq_word, 1000), fontFamily = "Helvetica", shape = "diamond")

4.2 Gauge Sentiment

Previously we got tidy_data, now we use it for sentiment analysis.

tweet_sentiment <- tidy_data %>%
  inner_join(get_sentiments("bing"), by = c(term = "word"))

Because we chose bing lexicon, all terms will be categorized in a binary fashion into positive or negative.

tweet_sentiment

The result shows each term in each document has its sentiment category – positive or negative. To visualise the mose frequent positive and negative words, we should group our data by sentiment and term with sum value for count.

tweet_sentiment %>%
  count(sentiment, term, wt = count) %>% # count the number of frequency of terms
  filter(n > 200) %>% # only keep terms occur more than 200 times
  mutate(n = ifelse(sentiment == "negative", -n, n)) %>% # change the n of negative to -n
  mutate(term = reorder(term, n)) %>%
  ggplot(aes(term, n, fill = sentiment)) +
  geom_col() +
  ggplot2::theme_minimal() +
  ggplot2::theme(axis.text.x = ggplot2::element_blank()) +
  geom_text(aes(label = n, y = n - 50*sign(n)), color = "#FFFFFF", size = 3) +
  ggplot2::labs(
    x = NULL, y = NULL,
    title = "Most frequent words per sentiment",
    caption = "\nSource: Data collected from Twitter's REST API via rtweet") +
  coord_flip() # reverse x/y axis

We can make a comparison wordcloud to show the most frequent postive and negative words at the same plot.

tidy_data %>%
  inner_join(get_sentiments("bing"), by = c(term = "word")) %>%
  count(term, sentiment, sort = TRUE) %>%
  acast(term ~ sentiment, value.var = "n", fill = 0) %>% # change molten frame to wide frame
  subset(select = c(2, 1)) %>% # put positive before negative by reorder columns
  comparison.cloud(max.words = 100,
                   rot.per = 0,
                   match.colors = TRUE,
                   random.order = FALSE
                   )

By comprison wordcloud, we can clearly see the two category words. Positive high frequent words include thank, protect, safe, love, support. Negative high frequent words include death, die, risk, outbreak, crisis, symptom.

The nrc lexicon can catogerize words with 10 sentiments.

# list all sentiment categories by nrc type
unique(get_sentiments("nrc")$sentiment)
 [1] "trust"        "fear"         "negative"     "sadness"      "anger"       
 [6] "surprise"     "positive"     "disgust"      "joy"          "anticipation"

So we can make a comparison wordcloud for the other 8 sentiments excluding positive or negative.

data_tidy <- tidy_data %>%
  # Inner join to nrc lexicon
  inner_join(get_sentiments("nrc"), by = c("term" = "word")) %>% 
  
  # Drop positive or negative
  filter(!grepl("positive|negative", sentiment)) %>% 
  
  # Count by sentiment and term
  count(sentiment, term) %>% 
  
  # Spread sentiment, using n for values
  spread(sentiment, n, fill = 0)  %>% 
  
  # Convert to data.frame, making term the row names
  data.frame(row.names = "term")

# Plot comparison cloud
comparison.cloud(data_tidy, 
                 max.words = 100, 
                 title.size = 1,
                 random.order = FALSE, # more frequent words will be in center
                 rot.per = 0.0, # proportion words with 90 degree rotation
                 colors = brewer.pal(8, "Set2") # Set color theme with 5 colors
                 )

Most words under each sentiment are correctly associated wit the sentiment. It’s a surprise that the word trump belong to surprise sentiment. Maybe it is not a surprise as he always give us surprise :).

4.3 Association Rules

By assoiciation rules analysis, we can find the words associated with specific word. Firstly we need to remove the terms having high sparsity (0.999).

# remove the terms having 0.999 sparsity, only terms occuring in 0.1% documents are retained
new_doc_term <- removeSparseTerms(dt_cv_corpus, 0.99)

new_doc_term
<<DocumentTermMatrix (documents: 17947, terms: 197)>>
Non-/sparse entries: 66405/3469154
Sparsity           : 98%
Maximal term length: 20
Weighting          : term frequency (tf)
# Convert to matrix
new_mat <- as.matrix(new_doc_term)

197 terms fulfill our filtering requirement. Now we can check the associated words. As the term need is the most frequent word, let’s see what people need most.

# Use the findAssocs function to find words associated with the 'need'
findAssocs(new_doc_term, "need", 0.03)
$need
   help protect     act   fight    keep   pleas    care    know    nurs     ppe respons 
   0.08    0.08    0.06    0.06    0.06    0.05    0.04    0.04    0.04    0.04    0.04 
   save    also    much    safe    stop 
   0.04    0.03    0.03    0.03    0.03 

It is not suprised to see that people need help, need to take care and protect themselves from the virus, need to keep safe and need ppe. For those infected, they need nurse to help.

Now we can check what terms are associated with safe, as it is important for everyone to keep safe under COVID-19 outbreak.

# Use the findAssocs function to find words associated with the 'patient'
findAssocs(new_doc_term, "safe", 0.03)
$safe
     stay      keep      home   everyon      hope communiti     pleas      care      dure 
     0.30      0.20      0.09      0.07      0.07      0.06      0.06      0.05      0.05 
  continu    friend      love   stayhom      best    famili       let      need     thank 
     0.04      0.04      0.04      0.04      0.03      0.03      0.03      0.03      0.03 

The most associated words with safe are stay, keep and home. Actually, keeping stay home is the most efficient way to protect ourselves from the virus. Therefore, from COVID-19 tweets data, I think we should know that it is important to stay home under current serious situation.

4.5 Topic Modelling

sum_rows <- apply(new_doc_term, 1, sum)

# remove documents without words
dtm_nonzeros <- new_doc_term[sum_rows > 0, ]
# apply LDA function and set the number of topics to 4
lda <- LDA(dtm_nonzeros, k = 4)
# get the first 10 terms of each topic
term <- terms(lda, 10)

term
      Topic 1    Topic 2   Topic 3  Topic 4  
 [1,] "death"    "stay"    "china"  "home"   
 [2,] "test"     "think"   "need"   "pandem" 
 [3,] "dure"     "help"    "pandem" "trump"  
 [4,] "support"  "need"    "live"   "know"   
 [5,] "posit"    "pleas"   "stop"   "respons"
 [6,] "trump"    "due"     "famili" "help"   
 [7,] "lockdown" "work"    "health" "work"   
 [8,] "fight"    "test"    "dure"   "need"   
 [9,] "today"    "health"  "help"   "live"   
[10,] "die"      "stayhom" "state"  "good"   

We can visualize the first 10 terms of each topic.

# construct a tidy data frame of the LDA model result by tidy function from tidytext package
topics <- tidy(lda, matrix = "beta")

# generate the top 10 terms of each topic
top_terms <- topics %>%
  group_by(topic) %>%
  top_n(10, beta) %>%
  ungroup() %>%
  arrange(topic, -beta)

# plot the first 10 terms by beta value
top_terms %>%
  mutate(term = reorder_within(term, beta, topic)) %>%
  ggplot(aes(term, beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  ggplot2::labs(
    x = NULL,
    title = "Top 10 terms in each topic",
    caption = "\nSource: Data collected from Twitter's REST API via rtweet") +
  coord_flip() +
  scale_x_reordered()

The beta value tells us probability of that term (word) belonging to that topic.

We can see some terms like help, need, trump and pandemic appear in more than 1 topic, this is not surprised. There are differences between these collections of terms. - Topic 1 is about test, die from positive, like a topic about the new cases and death report. - Topic 2 is about stay home, need and help, like a topic about what people do. - Topic 3 is more related to china. We know that recently the spreading pace of the pandemic in China is almost stopped. - In topic 4, the terms trump, know and good appear together.

5. Conclusion

By using a combination of Bag of Words analysis, sentiment analysis, association rules and topic modeling, we have come to a good understanding of the latest COVID-19 tweets.

PS: In addition, we find that trump is a ‘surprise’ term and the terms trump, know and good are in same topic by our modelling. I think this is a good modelling, as ‘actually’ no body knows coronavirus better than he does, just like in below video. :)

library("htmltools")
library("vembedr")
embed_url("https://www.youtube.com/watch?v=sR3f95BGIiA")
LS0tCnRpdGxlOiAiVGV4dCBBbmFseXNpcyBQcm9qZWN0IC0tIENPVklELTE5IFR3ZWV0cyBBbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQohW10oaHR0cHM6Ly9hcGkudGltZS5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMjAvMDMvc2luZ2Fwb3JlX2Nvcm9uYXZpcnVzX2NvdmlkMTkuanBnP3c9ODAwJnF1YWxpdHk9ODUpCgpUaGUgQ09WSUQtMTkgb3V0YnJlYWsgaXMgYW4gdW5wcmVjZWRlbnRlZCBnbG9iYWwgcHVibGljIGhlYWx0aCBjaGFsbGVuZ2UuIEl0IGlzIGEgaGVhdGVkIHRvcGljIGluIHNvY2lhbCBuZXR3b3Jrcy4gUGVvcGxlIGRpc2N1c3MgaXQgYW5kIHNoYXJlIHRoZSBuZXdzIGFib3V0IGl0IGFzIHRoZSBudW1iZXIgb2YgbmV3IGNhc2VzIGFyZSBzb2FyaW5nLiBJbiBvcmRlciB0byBhbmFseXNlIGhvdyBwZW9wbGUgZGlzY3VzcyBpdCBpbiBUd2lpdGVyIGFuZCB3aGF0J3MgcGVvcGxlJ3MgcmVzcG9uc2UsIEkgd2lsbCB1c2UgdGV4dCBhbmFseXNpcyB0b29scyBpbiB0aGlzIGFydGljbGUuCgpNeSBtaXNzaW9uIGlzIHRvIGFxdWlyZSB0aGUgbGF0ZXN0IENPVklELTE5IHR3ZWV0cyBkYXRhLCBjbGVhbiB0aGUgZGF0YSBmb3IgYW5hbHlzaXMgd2l0aCBkYXRhIHRyYW5zZm9ybWF0aW9uLCB0aGVuIGRvIG1vZGVsbGluZyBieSB0ZXh0IGFuYWx5c2lzIG1ldGhvZHMgc3VjaCBhcyBCYWcgb2YgV29yZHMsIFNlbnRpbWVudCBBbmFseXNpcywgQXNzb2NpYXRpb24gUnVsZXMgYW5kIFRvcGljIE1vZGVsbGluZy4KCkxldCdzIHN0YXJ0IHdpdGggZXh0cmFjdGluZyBkYXRhIGZyb20gVHdpdHRlci4KCiMgMS4gRGF0YSBPYnRhaW5pbmcKYGBge3IsIHdhcm5pbmc9RkFMU0V9CiMgbG9hZCBsaWJyYXJpZXMKIyBmb3IgZGF0YSBhcXVpcmluZwpsaWJyYXJ5KHJ0d2VldCkgIyB0byByZXR1cm4gdHdlZXRzIGRhdGEKCiMgZm9yIGRhdGEgY2xlYW5pbmcKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkodGV4dGNsZWFuKQoKIyBmb3IgZGF0YSB2aXN1YWxpemF0aW9uCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeSh3b3JkY2xvdWQpCgojIGZvciB0ZXh0IGFuYWx5c2lzCmxpYnJhcnkodGlkeXRleHQpICMgdGV4dCBtaW5pbmcKbGlicmFyeSh0bSkgIyB0ZXh0IG1pbmluZyBmb3IgY3JlYXRpbmcgY29ycHVzCmxpYnJhcnkodG9waWNtb2RlbHMpICMgdG9waWMgbW9kZWwgcGFja2FnZQpsaWJyYXJ5KHRleHRkYXRhKSAjIGEgZnJhbWV3b3JkIHRvIGRlYWwgd2l0aCB0ZXh0IGRhdGFzZXQKYGBgCgoKYGBge3J9CiMgRXh0cmFjdCBkYXRhIGJ5IGZ1bmN0aW9uIG9mIHJ0d2VldApydCA8LSBzZWFyY2hfdHdlZXRzKAogICMgU2V0IHNlYXJjaCBrZXl3b3JkCiAgcSA9ICIjQ09WSUQxOSIsIAogIAogICMgU2V0IHRoZSBudW1iZXIgb2YgdHdlZXRzIHRvIHJldHVybgogIG4gPSAxODAwMCwgCiAgCiAgIyBTcGVjaWZ5IHRoZSB0eXBlIG9mIHNlYXJjaCByZXN1bHRzCiAgdHlwZSA9ICJtaXhlZCIsCiAgCiAgIyBEbyBub3QgaW5jbHVkZSByZXR3ZWV0cwogIGluY2x1ZGVfcnRzID0gRkFMU0UsIAogIAogICMgU2V0IHR3ZWV0IGxhbmd1YWdlIHRvIEVuZ2xpc2gKICBsYW5nID0gImVuIiwgCiAgCiAgIyBUd2l0dGVyIHJhdGUgbGltaXRzIGNhcCB0aGUgbnVtYmVyIG9mIHNlYXJjaCByZXN1bHRzIHJldHVybmVkIHRvIDE4LDAwMCBldmVyeSAxNSBtaW51dGVzLiBUbyByZXF1ZXN0IG1vcmUgdGhhbiB0aGF0LCBzaW1wbHkgc2V0IHJldHJ5b25yYXRlbGltaXQgPSBUUlVFIGFuZCBydHdlZXQgd2lsbCB3YWl0IGZvciByYXRlIGxpbWl0IHJlc2V0cy4KICByZXRyeW9ucmF0ZWxpbWl0ID0gVFJVRQopCmBgYAoKYGBge3J9CiMgc2F2ZSB0aGUgcmV0dXJuZWQgZGF0YSB0byBjc3YgZm9yIGZ1dHVyZSB1c2UKc2F2ZV9hc19jc3YocnQsICJjb3ZpZDE5LmNzdiIpCmBgYAoKQWZ0ZXIgc2F2aW5nIHRoZSB0d2VldHMgZGF0YSwgd2UgY2FuIGxvYWQgaXQgZGlyZWN0bHkuCmBgYHtyfQpydCA8LSByZWFkLmNzdihmaWxlID0gImNvdmlkMTkuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCmBgYHtyfQojIGNoZWNrIHRoZSBkaW1lbnNpb24gb2YgcmV0dXJuZWQgdHdlZXRzIGRhdGEKZGltKHJ0KQpgYGAKVGhlcmUgYXJlIDE3OTQ3IHR3ZWV0cyByZXR1cm5lZCB0byBvdXIgZGF0YWZyYW1lIHdpdGggOTAgdmFyaWFibGVzLgoKYGBge3J9CiMgVmlldyB0aGUgZGF0YWZyYW1lClZpZXcocnQpCmBgYAoKQmVjYXVzZSB3ZSBzcGVjaWZpZWQgdGhlIHNlYXJjaCByZXN1bHRzIGJ5IGAibWl4ZWQiYCwgc28gbGV0J3Mgb3JkZXIgdGhlIHR3ZWV0cyBieSBwb3N0IHRpbWUgYW5kIGNoZWNrIHRoZSBmaXJzdCBhbmQgbGFzdCAxMCB0d2VldHMuCmBgYHtyfQojIGNoZWNrIHRoZSBmaXJzdCAxMCByb3dzIGJ5IG9yZGVyaW5nIGNyZWF0ZWRfYXQgdGltZQpoZWFkKHJ0W29yZGVyKHJ0JGNyZWF0ZWRfYXQpLF0sIDEwKQpgYGAKCmBgYHtyfQojIGNoZWNrIHRoZSBsYXN0IDEwIHJvd3MgYnkgb3JkZXJpbmcgY3JlYXRlZF9hdCB0aW1lCnRhaWwocnRbb3JkZXIocnQkY3JlYXRlZF9hdCksXSwgMTApCmBgYApPbmx5IDMgdHdlZXRzIHdlcmUgcG9zdGVkIGF0IGxlYXN0IDEwIGhvdXJzIHRoYW4gb3RoZXJzLiBTbyB3ZSBjYW4gcGxvdCB0aGUgbnVtYmVyIG9mIHR3ZWV0cyBjcmVhdGVkIGJ5IHRpbWUgdG8gc2VlIHRoZSB0cmVuZC4KCmBgYHtyfQojIFBsb3QgdGhlIGZyZXF1ZW5jeSBvZiB0d2VldHMgYWZ0ZXIgMjAyMC0wMy0yOCAyMzowMDowMCB3aXRoIGludGVydmFsIDEgbWluCnRzX3Bsb3QocnQgJT4lIGZpbHRlcihjcmVhdGVkX2F0ID4gIjIwMjAtMDMtMjggMjM6MDA6MDAiKSwgCiAgICAgICAgYnkgPSAibWlucyIsIAogICAgICAgIHRyaW0gPSAxLCAKICAgICAgICBjb2xvciA9ICIjMDBhY2VlIiAjIHVzZSB0d2l0dGVyIGxvZ28gYmx1ZQogICAgICAgICkgKwogICAgICAgIGdncGxvdDI6OnRoZW1lX21pbmltYWwoKSArCiAgICAgICAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkgKwogICAgICAgIGdncGxvdDI6OmxhYnMoCiAgICAgICAgICB4ID0gTlVMTCwgeSA9IE5VTEwsCiAgICAgICAgICB0aXRsZSA9ICJGcmVxdWVuY3kgb2YgI0NPVklEMTkgdHdlZXRzIGZyb20gcGFzdCBob3VycyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJUd2l0dGVycyBjb3VudHMgYWdncmVnYXRlZCB1c2luZyAxIG1pbnMgaW50ZXJ2YWxzIiwKICAgICAgICAgIGNhcHRpb24gPSAiXG5Tb3VyY2U6IERhdGEgY29sbGVjdGVkIGZyb20gVHdpdHRlcidzIFJFU1QgQVBJIHZpYSBydHdlZXQiCiAgICAgICAgICApCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZSBudW1iZXIgb2YgdHdlZXRzIGNyZWF0ZWQgcGVyIG1pbnV0ZXMgaXMgaW4gNTAgdG8gMTUwIHJhbmdlLiBBdCBldmVyeSBvJ2Nsb2NrIGFuZCBoYWxmIHBhc3QgbydjbG9jaywgdGhlIG51bWJlciBvZiB0d2VldHMgcHVibGlzaGVkIGlzIG11Y2ggaGlnaGVyIHRoYW4gb3RoZXIgdGltaW5nLgoKCiMgMi4gRGF0YSBUcmFuc2Zvcm1hdGlvbgoKV2Ugd2lsbCB1c2UgdGhlIHRleHQgY29sdW1uIG9mIG91ciBgcnRgIGRhdGFzZXQgZm9yIHRleHQgYW5hbHlzaXMgc28gaXQgaXMgbmVlZGVkIHRvIHRyYW5zZm9ybSB0aGUgdGV4dCBjb2x1bW4gdG8gY29ycHVzIHR5cGUuCmBgYHtyfQojIHRyYW5zZm9ybSBvdXIgZGF0YSB0byBzb3VyY2UgZm9ybWF0CmNvdmlkIDwtIFZlY3RvclNvdXJjZShydCR0ZXh0KQoKIyBjaGVjayB0aGUgZmlyc3QgdHdlZXQKY292aWRbMV0KYGBgCgpgYGB7cn0KIyB0cmFuc2Zvcm0gdGhlIHNvdXJjZSBmaWxlIGludG8gYSBDb3JwdXMKY3ZfY29ycHVzIDwtIFZDb3JwdXMoY292aWQpCgojIHRoZW4gaW5zcGVjdCB0aGUgZmlyc3Qgb25lIHR3ZWV0Cmluc3BlY3QoY3ZfY29ycHVzWzFdKQpgYGAKCiMgMy4gRGF0YSBDbGVhbmluZwoKTm93IHRleHQgY2xlYW5pbmcgaXMgdG8gYmUgY29uZHVjdGVkLCBhcyBmb3IgdGV4dCBhbmFseXNpcyBpdCBpcyBuZWNlc3NhcnkgdG8gcmVtb3ZlIHB1bmN0dWF0aW9uLCBudW1iZXJzLCB3aGl0ZSBzcGFjZSwgc3RvcCB3b3JkcyBhbmQgc3BlY2lmaWVkIHdvcmRzIHN1Y2ggYXMgInZpcnVzIiwgImNvdmlkIiwgImNvcm9uYXZpcnVzIi4gVGhlcmVmb3IsIHdlIGNhbiBjcmVhdGUgYSBmdW5jdGlvbiB0byBjbGVhbiBvdXIgdGV4dCBkYXRhLgpgYGB7cn0KY2xlYW5fY29ycHVzIDwtIGZ1bmN0aW9uKGNvcnApIHsKICAKICAjIHJlbW92ZSBhbnkgaHRtbCBmb3JtYXQgaWYgYW55CiAgY29ycCA8LSB0bV9tYXAoY29ycCwgY29udGVudF90cmFuc2Zvcm1lcihyZXBsYWNlX2h0bWwpKQogIAogICMgcmVtb3ZlIGFueSBVUkxzCiAgY29ycCA8LSB0bV9tYXAoY29ycCwgY29udGVudF90cmFuc2Zvcm1lcihyZXBsYWNlX3VybCkpICAKICAKICAjIHRyYW5zZm9ybSBhbGwgY2hhcmFjdGVycyB0byBsb3dlciBjYXNlCiAgY29ycCA8LSB0bV9tYXAoY29ycCwgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkKICAKICAjIHdvcmQgc3RlbW1pbmcKICBjb3JwIDwtIHRtX21hcChjb3JwLCBzdGVtRG9jdW1lbnQpICAKICAKICAjIHJlbW92ZSBwdW5jdHVhdGlvbgogIGNvcnAgPC0gdG1fbWFwKGNvcnAsIHJlbW92ZVB1bmN0dWF0aW9uKQoKICAjIHJlbW92ZSBudW1iZXJzCiAgY29ycCA8LSB0bV9tYXAoY29ycCwgcmVtb3ZlTnVtYmVycykgIAogIAogICMgc3RyaXAgZXh0cmEgd2hpdGVzcGFjZQogIGNvcnAgPC0gdG1fbWFwKGNvcnAsIHN0cmlwV2hpdGVzcGFjZSkKICAKICAjIFJlbW92ZSBlbmdsaXNoIGNvbW1vbiBzdG9wd29yZHMKICBjb3JwIDwtIHRtX21hcChjb3JwLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpCiAgCiAgIyByZW1vdmUgc3BlY2lmaWVkIHdvcmRzCiAgY29ycCA8LSB0bV9tYXAoY29ycCwgcmVtb3ZlV29yZHMsIAogICAgICAgICAgICAgICAgIGMoImNvdmlkMTkiLCAiY29yb25hdmlydXMiLCAiY292aWQiLCAid2lsbCIsICJjYW4iLCAibm93IiwgImdldCIsICJqdXN0IiwKICAgICAgICAgICAgICAgICAgICJuZXciLCAicGVvcGwiLCAib25lIiwgInZpcnVzIiwgImNhc2UiLCAiZGF5IiwgInRpbWUiLCAicGVuZGVtIiwgImxpa2UiLAogICAgICAgICAgICAgICAgICAgInRha2UiLCAibWFrZSIsICJzYXkiLCAic2VlIikpCgogIHJldHVybihjb3JwKQp9CmBgYAoKYGBge3J9CiMgQXBwbHkgdGhlIGZ1bmN0aW9uIHRvIG91ciBjb3JwdXMgZGF0YQpjdl9jb3JwdXNfY2xlYW5lZCA8LSBjbGVhbl9jb3JwdXMoY3ZfY29ycHVzKQpgYGAKCmBgYHtyfQojIGNvbXBhcmUgdGhlIGZpcnN0IHR3ZWV0IGJlZm9yZSBhbmQgYWZ0ZXIgY2xlYW5pbmcKY2F0KCJCZWZvcmU6XG4iLCBjb250ZW50KGN2X2NvcnB1c1tbMV1dKSwgIlxuXG4iKQoKY2F0KCJBZnRlcjpcbiIsIGNvbnRlbnQoY3ZfY29ycHVzX2NsZWFuZWRbWzFdXSkpCmBgYApPdXIgZnVuY3Rpb24gc3VjY2Vzc2Z1bGx5IGNoYW5nZWQgYWxsIGxldHRlcnMgdG8gbG93ZXJjYXNlLCByZW1vdmVkIHRoZSBudW1iZXJzLCB1cmwsIHB1bmN0dWF0aW9uLCBzdG9wIHdvcmRzIGFuZCB0aGUgc3BlY2lmaWVkIHdvcmRzLiBJbiBhZGRpdGlvbiwgdGhlIHN0ZW1taW5nIHdhcyBjb25kdWN0ZWQgZm9yIHdvcmRzIGxpa2UgInJlcG9ydGVkIiwgImNvbmZpcm1lZCIsICJkaXNjaGFyZ2VkIi4gTm93IHdlIGNhbiBzdGFydCBvdXIgbW9kZWxsaW5nIHdvcmsuCgoKIyA0LiBNb2RlbGxpbmcKCkluIG1vZGVsbGluZyBwYXJ0LCB3ZSB3aWxsIGFwcGx5IGZvdXIgbWV0aG9kcyB0byB1bmRlcnN0YW5kIHRoZSBDT1ZJRC0xOSB0d2VldHMgZGF0YS4KMS4gQmFnIG9mIFdvcmRzCjIuIEdhdWdlIFNlbnRpbWVudAozLiBBc3NvY2lhdGlvbiBSdWxlcwo0LiBUb3BpYyBNb2RlbGxpbmcKCiMjIDQuMSBCYWcgb2YgV29yZHMgTW9kZWwKYGBge3J9CiMgdHJhbnNmb3JtIHRoZSBjbGVhbmVkIGRhdGEgaW50byBhIGRvYy10ZXJtIG1hdHJpeApkdF9jdl9jb3JwdXMgPC0gRG9jdW1lbnRUZXJtTWF0cml4KGN2X2NvcnB1c19jbGVhbmVkKQoKIyBwcmludCB0aGUgZGltZW5zaW9uIG9mIHRoZSBtYXRyaXgKZGltKGR0X2N2X2NvcnB1cykKYGBgCldlIGhhdmUgMTc5NTEgdHdlZXRzIGFuZCAzODk2MiBkaWZmZXJlbnQgd29yZHMvdGVybXMuCgpgYGB7cn0KIyB0cmFuc2Zvcm0gdG8gbWF0cml4IGZvcm1hdAptdF9jdl9jb3JwdXMgPC0gYXMubWF0cml4KGR0X2N2X2NvcnB1cykKYGBgCgpgYGB7cn0KIyB2aWV3IGEgcG9ydGlvbiBvZiB0aGUgbWF0cml4Cm10X2N2X2NvcnB1c1sxOjEwLCA1MDo1NV0KYGBgCmBgYHtyfQojIHZpc3VhbGlzZSB0d2l0dGVyIGRhdGEKIyBjb252ZXJ0IGZyb20gbWF0cml4IHRvIHRpZHkgZGF0YQp0aWR5X2RhdGEgPC0gdGlkeShkdF9jdl9jb3JwdXMpCgp0aWR5X2RhdGEKCiMgdGhlIHRpZHlfZGF0YSB3b3VsZCBiZSB1c2VkIGZvciB0aGUgc2VudGltZW50IGFuYWx5c2lzIGluIHRoZSBsYXRlciBzZWN0aW9uLgpgYGAKYGBge3J9CnRlcm1fZnJlIDwtIGNvbFN1bXMobXRfY3ZfY29ycHVzKQpgYGAKCmBgYHtyfQojIHNvcnQgdGVybSBmcmVxdWVuY3kgaW4gZGVjcmVhc2luZyBvcmRlcgp0ZXJtX2ZyZSA8LSBzb3J0KHRlcm1fZnJlLCBkZWNyZWFzaW5nID0gVFJVRSkKYGBgCgpgYGB7cn0KIyBjaGFuZ2UgdGhlIHRlcm1fZnJlIHRvIGRhdGFmcmFtZSB0eXBlCmZyZXFfd29yZCA8LSBkYXRhLmZyYW1lKHdvcmQgPSBuYW1lcyh0ZXJtX2ZyZSksIGZyZXEgPSB0ZXJtX2ZyZSkKCiMgdmlldyB0aGUgMjAgbW9zdCBmcmVxdWVudCB0ZXJtcwpoZWFkKGZyZXFfd29yZCwgMjApCmBgYAoKYGBge3J9CiMgcGxvdCB0aGUgbW9zdCBmcmVxdWVudCAyMCB0ZXJtcyBpbiBvdXIgZGF0YQpnZ3Bsb3QoaGVhZChmcmVxX3dvcmQsIDIwKSwgYWVzKHggPSByZW9yZGVyKHdvcmQsIGZyZXEpLCB5ID0gZnJlcSksIGZpbGwgPSAiYmx1ZSIpICsgCiAgZ2VvbV9jb2woZmlsbCA9ICIjMDBhY2VlIikgKwogIGdncGxvdDI6OnRoZW1lX21pbmltYWwoKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF9ibGFuaygpKSArCiAgZ2dwbG90Mjo6bGFicygKICAgIHggPSBOVUxMLCB5ID0gTlVMTCwKICAgIHRpdGxlID0gIk1vc3QgZnJlcXVlbnQgMjAgd29yZHMgaW4gI0NPVklEMTkgdHdlZXRzXG4iLAogICAgY2FwdGlvbiA9ICJcblNvdXJjZTogRGF0YSBjb2xsZWN0ZWQgZnJvbSBUd2l0dGVyJ3MgUkVTVCBBUEkgdmlhIHJ0d2VldCIKICApICsKICBjb29yZF9mbGlwKCkgKyAKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZnJlcSksIGhqdXN0ID0gKzEuMiwgY29sb3IgPSAiI0ZGRkZGRiIsIHNpemUgPSAzKQpgYGAKCkhhdmluZyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggdGVybXMsIGxldCdzIG1ha2UgYSB3b3JkY2xvdWQgdG8gdmlzdWFsaXNlIHRoZSBtb3N0IGZyZXF1ZW50IHRlcm1zLgpgYGB7cn0KIyBjcmVhdGUgYSB2ZWN0b3Igb2YgdGVybSBmcmVxdWVuY3kgdmFsdWVzCnRlcm1zX3ZlYyA8LSBuYW1lcyh0ZXJtX2ZyZSkKYGBgCgpgYGB7cn0KIyBjcmVhdGUgd29yZGNsb3VkIGZvciB0aGUgODAgbW9zdCBmcmVxdWVudCB3b3JkcyB3aXRoIG1pbmltYWwgZnJlcXVlbmN5IG92ZXIgMzAwCndvcmRjbG91ZCh3b3JkcyA9IHRlcm1zX3ZlYywKICAgICAgICAgIGZyZXEgPSB0ZXJtX2ZyZSwKICAgICAgICAgIG1pbi5mcmVxID0gMzAwLAogICAgICAgICAgbWF4LndvcmRzID0gODAsCiAgICAgICAgICBzY2FsZSA9IGMoMywwLjEzKSwgIyBzY2FsZSB0aGUgc2l6ZSBvZiBkaWZmZXJlbnQgbGV2ZWwgb2YgZnJlcXVlbmN5CiAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwgIyBtb3JlIGZyZXF1ZW50IHdvcmRzIHdpbGwgYmUgaW4gY2VudGVyCiAgICAgICAgICByb3QucGVyID0gMC4wLCAjIHByb3BvcnRpb24gd29yZHMgd2l0aCA5MCBkZWdyZWUgcm90YXRpb24KICAgICAgICAgIGNvbG9ycyA9IGJyZXdlci5wYWwoNiwgIlNldDIiKSkgIyBTZXQgY29sb3IgdGhlbWUgd2l0aCA1IGNvbG9ycwpgYGAKClRoZSB3b3JkbG91ZCBzaG93IHRoZSBtb3N0IGZyZXF1ZW50IHdvcmRzIGluIHRoZSBtaWRkbGUuIFdlIGNhbiBzZWUgd29yZHMgc3VjaCBhcyBgbmVlZGAsIGBoZWxwYCwgYGR1cmVgLCBgdGVzdGAsIGBkZWF0aGAsIGBzdGF5YCwgYGhvbWVgLiBUaGVzZSB3b3JkcyBhcmUgZGVmaW5pdGVseSBhc3NvY2lhdGVkIHdpdGggY29yb25hdmlydXMgYXMgd2Uga25vdyB0aGUgc2l0dWF0aW9uIGFyb3VuZCB0aGUgYHdvcmxkYC4gTWFueSBwZW9wbGUgbmVlZCBgaGVscGAgYW5kIGN1cmUgYXMgdGhleSBoYWQgdGFrZW4gYHRlc3RgIGFuZCBpbmNyZWFzaW5nIG51bWJlcnMgb2YgcGVvcGxlIHdlcmUgcG9zaXRpdmUuIE1hbnkgcGVvcGxlIGFyZSBgZHVyZWx5YCBzdWZmZXJpbmcgZnJvbSB0aGUgYHBhbmRlbWljYC4gVGhlIHZpcnVzIGhhcyBsZWFkIHRvIG1vcmUgdGhhbiAxMCB0aG91c2FuZCBgZGVhdGhgLiBUbyBgcHJvdGVjdGAgb3Vyc2VsdmVzLCBpdCBpcyBiZXR0ZXIgdG8gYHN0YXkgaG9tZWAgYW5kIGB3b3JrYCBmcm9tIGhvbWUuCgpBbHRlcm5hdGl2ZSB3YXkgdG8gdmlzdWFsaXNlIHRoZSB3b3JkY2xvdWQgYnkgYHdvcmRjbG91ZDJgIHBhY2thZ2UuIEl0IGNyZWF0ZXMgYSB3aWRnZXQgYW5kIGdlbmVyYXRlIHRoZSB3b3JkcyBmcm9tIG1vc3QgZnJlcXVlbnQgdG8gbGVzcyBmcmVxdWVudC4KYGBge3J9CmxpYnJhcnkod29yZGNsb3VkMikKd29yZGNsb3VkMihkYXRhID0gaGVhZChmcmVxX3dvcmQsIDEwMDApLCBmb250RmFtaWx5ID0gIkhlbHZldGljYSIsIHNoYXBlID0gImRpYW1vbmQiKQpgYGAKCgojIyA0LjIgR2F1Z2UgU2VudGltZW50CgpQcmV2aW91c2x5IHdlIGdvdCBgdGlkeV9kYXRhYCwgbm93IHdlIHVzZSBpdCBmb3Igc2VudGltZW50IGFuYWx5c2lzLgpgYGB7cn0KIyBqb2luIHRoZSB0aWR5IGRhdGEgd2l0aCB0aGUgY29ycmVzcG9uZGluZyBzZW50aW1lbnQKdHdlZXRfc2VudGltZW50IDwtIHRpZHlfZGF0YSAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIiksIGJ5ID0gYyh0ZXJtID0gIndvcmQiKSkgIyBzZXQgc2VudGltZW50IGxleGljb24gdG8gYmluZwpgYGAKCkJlY2F1c2Ugd2UgY2hvc2UgYGJpbmdgIGxleGljb24sIGFsbCB0ZXJtcyB3aWxsIGJlIGNhdGVnb3JpemVkIGluIGEgYmluYXJ5IGZhc2hpb24gaW50byBwb3NpdGl2ZSBvciBuZWdhdGl2ZS4KYGBge3J9CiMgaW5zcGVjdCB0aGUgcmVzdWx0CnR3ZWV0X3NlbnRpbWVudApgYGAKClRoZSByZXN1bHQgc2hvd3MgZWFjaCB0ZXJtIGluIGVhY2ggZG9jdW1lbnQgaGFzIGl0cyBzZW50aW1lbnQgY2F0ZWdvcnkgLS0gYHBvc2l0aXZlYCBvciBgbmVnYXRpdmVgLiBUbyB2aXN1YWxpc2UgdGhlIG1vc2UgZnJlcXVlbnQgYHBvc2l0aXZlYCBhbmQgYG5lZ2F0aXZlYCB3b3Jkcywgd2Ugc2hvdWxkIGdyb3VwIG91ciBkYXRhIGJ5IGBzZW50aW1lbnRgIGFuZCBgdGVybWAgd2l0aCBzdW0gdmFsdWUgZm9yIGBjb3VudGAuCgpgYGB7cn0KdHdlZXRfc2VudGltZW50ICU+JQogICMgc3VtIHRoZSBjb3VudCBvZiBlYWNoIHRlcm1zIHBlciBzZW50aW1lbnQKICBjb3VudChzZW50aW1lbnQsIHRlcm0sIHd0ID0gY291bnQpICU+JSAjIGNvdW50IHRoZSBudW1iZXIgb2YgZnJlcXVlbmN5IG9mIHRlcm1zCiAgZmlsdGVyKG4gPiAyMDApICU+JSAjIG9ubHkga2VlcCB0ZXJtcyBvY2N1ciBtb3JlIHRoYW4gMjAwIHRpbWVzCiAgbXV0YXRlKG4gPSBpZmVsc2Uoc2VudGltZW50ID09ICJuZWdhdGl2ZSIsIC1uLCBuKSkgJT4lICMgY2hhbmdlIHRoZSBuIG9mIG5lZ2F0aXZlIHRvIC1uCiAgbXV0YXRlKHRlcm0gPSByZW9yZGVyKHRlcm0sIG4pKSAlPiUKICAKICAjIHZpc3VhbGlzYXRpb24KICBnZ3Bsb3QoYWVzKHRlcm0sIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZ2VvbV9jb2woKSArICMgYmFyIGNoYXJ0CiAgZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCgpICsgIyBzZXQgcGxvdCB0aGVtZQogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF9ibGFuaygpKSArICMgZGVsZXRlIHggdGljayBsYWJlbHMKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbiwgeSA9IG4gLSA1MCpzaWduKG4pKSwgY29sb3IgPSAiI0ZGRkZGRiIsIHNpemUgPSAzKSArICMgYWRkIGRhdGEgbGFiZWwKICBnZ3Bsb3QyOjpsYWJzKAogICAgeCA9IE5VTEwsIHkgPSBOVUxMLCAjIHJlbW92ZSB4LCB5IGF4aXMgbGFiZWxzCiAgICB0aXRsZSA9ICJNb3N0IGZyZXF1ZW50IHdvcmRzIHBlciBzZW50aW1lbnQiLAogICAgY2FwdGlvbiA9ICJcblNvdXJjZTogRGF0YSBjb2xsZWN0ZWQgZnJvbSBUd2l0dGVyJ3MgUkVTVCBBUEkgdmlhIHJ0d2VldCIpICsKICBjb29yZF9mbGlwKCkgIyByZXZlcnNlIHgveSBheGlzCmBgYAoKV2UgY2FuIG1ha2UgYSBjb21wYXJpc29uIHdvcmRjbG91ZCB0byBzaG93IHRoZSBtb3N0IGZyZXF1ZW50IHBvc3RpdmUgYW5kIG5lZ2F0aXZlIHdvcmRzIGF0IHRoZSBzYW1lIHBsb3QuCmBgYHtyfQp0aWR5X2RhdGEgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpLCBieSA9IGModGVybSA9ICJ3b3JkIikpICU+JQogIGNvdW50KHRlcm0sIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogIGFjYXN0KHRlcm0gfiBzZW50aW1lbnQsIHZhbHVlLnZhciA9ICJuIiwgZmlsbCA9IDApICU+JSAjIGNoYW5nZSBtb2x0ZW4gZnJhbWUgdG8gd2lkZSBmcmFtZQogIHN1YnNldChzZWxlY3QgPSBjKDIsIDEpKSAlPiUgIyBwdXQgcG9zaXRpdmUgYmVmb3JlIG5lZ2F0aXZlIGJ5IHJlb3JkZXIgY29sdW1ucwogIGNvbXBhcmlzb24uY2xvdWQobWF4LndvcmRzID0gMTAwLAogICAgICAgICAgICAgICAgICAgcm90LnBlciA9IDAsCiAgICAgICAgICAgICAgICAgICBtYXRjaC5jb2xvcnMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgcmFuZG9tLm9yZGVyID0gRkFMU0UKICAgICAgICAgICAgICAgICAgICkKYGBgCkJ5IGNvbXByaXNvbiB3b3JkY2xvdWQsIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGUgdHdvIGNhdGVnb3J5IHdvcmRzLiBQb3NpdGl2ZSBoaWdoIGZyZXF1ZW50IHdvcmRzIGluY2x1ZGUgdGhhbmssIHByb3RlY3QsIHNhZmUsIGxvdmUsIHN1cHBvcnQuIE5lZ2F0aXZlIGhpZ2ggZnJlcXVlbnQgd29yZHMgaW5jbHVkZSBkZWF0aCwgZGllLCByaXNrLCBvdXRicmVhaywgY3Jpc2lzLCBzeW1wdG9tLgoKVGhlIGBucmNgIGxleGljb24gY2FuIGNhdG9nZXJpemUgd29yZHMgd2l0aCAxMCBzZW50aW1lbnRzLgpgYGB7cn0KIyBsaXN0IGFsbCBzZW50aW1lbnQgY2F0ZWdvcmllcyBieSBucmMgdHlwZQp1bmlxdWUoZ2V0X3NlbnRpbWVudHMoIm5yYyIpJHNlbnRpbWVudCkKYGBgCgpTbyB3ZSBjYW4gbWFrZSBhIGNvbXBhcmlzb24gd29yZGNsb3VkIGZvciB0aGUgb3RoZXIgOCBzZW50aW1lbnRzIGV4Y2x1ZGluZyBwb3NpdGl2ZSBvciBuZWdhdGl2ZS4KYGBge3J9CmRhdGFfdGlkeSA8LSB0aWR5X2RhdGEgJT4lCiAgIyBJbm5lciBqb2luIHRvIG5yYyBsZXhpY29uCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIiksIGJ5ID0gYygidGVybSIgPSAid29yZCIpKSAlPiUgCiAgCiAgIyBEcm9wIHBvc2l0aXZlIG9yIG5lZ2F0aXZlCiAgZmlsdGVyKCFncmVwbCgicG9zaXRpdmV8bmVnYXRpdmUiLCBzZW50aW1lbnQpKSAlPiUgCiAgCiAgIyBDb3VudCBieSBzZW50aW1lbnQgYW5kIHRlcm0KICBjb3VudChzZW50aW1lbnQsIHRlcm0pICU+JSAKICAKICAjIFNwcmVhZCBzZW50aW1lbnQsIHVzaW5nIG4gZm9yIHZhbHVlcwogIHNwcmVhZChzZW50aW1lbnQsIG4sIGZpbGwgPSAwKSAgJT4lIAogIAogICMgQ29udmVydCB0byBkYXRhLmZyYW1lLCBtYWtpbmcgdGVybSB0aGUgcm93IG5hbWVzCiAgZGF0YS5mcmFtZShyb3cubmFtZXMgPSAidGVybSIpCgojIFBsb3QgY29tcGFyaXNvbiBjbG91ZApjb21wYXJpc29uLmNsb3VkKGRhdGFfdGlkeSwgCiAgICAgICAgICAgICAgICAgbWF4LndvcmRzID0gMTAwLCAKICAgICAgICAgICAgICAgICB0aXRsZS5zaXplID0gMSwKICAgICAgICAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSwgIyBtb3JlIGZyZXF1ZW50IHdvcmRzIHdpbGwgYmUgaW4gY2VudGVyCiAgICAgICAgICAgICAgICAgcm90LnBlciA9IDAuMCwgIyBwcm9wb3J0aW9uIHdvcmRzIHdpdGggOTAgZGVncmVlIHJvdGF0aW9uCiAgICAgICAgICAgICAgICAgY29sb3JzID0gYnJld2VyLnBhbCg4LCAiU2V0MiIpICMgU2V0IGNvbG9yIHRoZW1lIHdpdGggOCBjb2xvcnMgZm9yIDggc2VudGltZW50cwogICAgICAgICAgICAgICAgICkKYGBgCgpNb3N0IHdvcmRzIHVuZGVyIGVhY2ggc2VudGltZW50IGFyZSBjb3JyZWN0bHkgYXNzb2NpYXRlZCB3aXQgdGhlIHNlbnRpbWVudC4gSXQncyBhIHN1cnByaXNlIHRoYXQKdGhlIHdvcmQgYHRydW1wYCBiZWxvbmcgdG8gYHN1cnByaXNlYCBzZW50aW1lbnQuIE1heWJlIGl0IGlzIG5vdCBhIHN1cnByaXNlIGFzIGhlIGFsd2F5cyBnaXZlIHVzIHN1cnByaXNlIDopLgoKCiMjIDQuMyBBc3NvY2lhdGlvbiBSdWxlcwoKQnkgYXNzb2ljaWF0aW9uIHJ1bGVzIGFuYWx5c2lzLCB3ZSBjYW4gZmluZCB0aGUgd29yZHMgYXNzb2NpYXRlZCB3aXRoIHNwZWNpZmljIHdvcmQuIEZpcnN0bHkgd2UgbmVlZCB0byByZW1vdmUgdGhlIHRlcm1zIGhhdmluZyBoaWdoIHNwYXJzaXR5ICgwLjk5OSkuCmBgYHtyfQojIHJlbW92ZSB0aGUgdGVybXMgaGF2aW5nIDAuOTk5IHNwYXJzaXR5LCBvbmx5IHRlcm1zIG9jY3VyaW5nIGluIDAuMSUgZG9jdW1lbnRzIGFyZSByZXRhaW5lZApuZXdfZG9jX3Rlcm0gPC0gcmVtb3ZlU3BhcnNlVGVybXMoZHRfY3ZfY29ycHVzLCAwLjk5KQoKbmV3X2RvY190ZXJtCgojIENvbnZlcnQgdG8gbWF0cml4Cm5ld19tYXQgPC0gYXMubWF0cml4KG5ld19kb2NfdGVybSkKYGBgCgoxOTcgdGVybXMgZnVsZmlsbCBvdXIgZmlsdGVyaW5nIHJlcXVpcmVtZW50LiBOb3cgd2UgY2FuIGNoZWNrIHRoZSBhc3NvY2lhdGVkIHdvcmRzLiBBcyB0aGUgdGVybSBgbmVlZGAgaXMgdGhlIG1vc3QgZnJlcXVlbnQgd29yZCwgbGV0J3Mgc2VlIHdoYXQgcGVvcGxlIGBuZWVkYCBtb3N0LgpgYGB7cn0KIyBVc2UgdGhlIGZpbmRBc3NvY3MgZnVuY3Rpb24gdG8gZmluZCB3b3JkcyBhc3NvY2lhdGVkIHdpdGggdGhlICduZWVkJwpmaW5kQXNzb2NzKG5ld19kb2NfdGVybSwgIm5lZWQiLCAwLjAzKQpgYGAKCkl0IGlzIG5vdCBzdXByaXNlZCB0byBzZWUgdGhhdCBwZW9wbGUgYG5lZWRgIGBoZWxwYCwgYG5lZWRgIHRvIHRha2UgYGNhcmVgIGFuZCBgcHJvdGVjdGAgdGhlbXNlbHZlcyBmcm9tIHRoZSB2aXJ1cywgYG5lZWRgIHRvIGBrZWVwYCBgc2FmZWAgYW5kIGBuZWVkYCBgcHBlYC4gRm9yIHRob3NlIGluZmVjdGVkLCB0aGV5IGBuZWVkYCBgbnVyc2VgIHRvIGBoZWxwYC4KCk5vdyB3ZSBjYW4gY2hlY2sgd2hhdCB0ZXJtcyBhcmUgYXNzb2NpYXRlZCB3aXRoIGBzYWZlYCwgYXMgaXQgaXMgaW1wb3J0YW50IGZvciBldmVyeW9uZSB0byBrZWVwIHNhZmUgdW5kZXIgQ09WSUQtMTkgb3V0YnJlYWsuCmBgYHtyfQojIFVzZSB0aGUgZmluZEFzc29jcyBmdW5jdGlvbiB0byBmaW5kIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCB0aGUgJ3NhZmUnCmZpbmRBc3NvY3MobmV3X2RvY190ZXJtLCAic2FmZSIsIDAuMDMpCmBgYApUaGUgbW9zdCBhc3NvY2lhdGVkIHdvcmRzIHdpdGggYHNhZmVgIGFyZSBgc3RheWAsIGBrZWVwYCBhbmQgYGhvbWVgLiBBY3R1YWxseSwgYGtlZXBpbmcgc3RheSBob21lYCBpcyB0aGUgbW9zdCBlZmZpY2llbnQgd2F5IHRvIHByb3RlY3Qgb3Vyc2VsdmVzIGZyb20gdGhlIHZpcnVzLiBUaGVyZWZvcmUsIGZyb20gQ09WSUQtMTkgdHdlZXRzIGRhdGEsIEkgdGhpbmsgd2Ugc2hvdWxkIGtub3cgdGhhdCBpdCBpcyBpbXBvcnRhbnQgdG8gc3RheSBob21lIHVuZGVyIGN1cnJlbnQgc2VyaW91cyBzaXR1YXRpb24uCgojIyA0LjUgVG9waWMgTW9kZWxsaW5nCmBgYHtyfQojIHN1bSB0aGUgd29yZHMgaW4gZWFjaCBkb2N1bWVudApzdW1fcm93cyA8LSBhcHBseShuZXdfZG9jX3Rlcm0sIDEsIHN1bSkKCiMgcmVtb3ZlIGRvY3VtZW50cyB3aXRob3V0IHdvcmRzCmR0bV9ub256ZXJvcyA8LSBuZXdfZG9jX3Rlcm1bc3VtX3Jvd3MgPiAwLCBdCmBgYAoKYGBge3J9CiMgYXBwbHkgTERBIGZ1bmN0aW9uIGFuZCBzZXQgdGhlIG51bWJlciBvZiB0b3BpY3MgdG8gNApsZGEgPC0gTERBKGR0bV9ub256ZXJvcywgayA9IDQpCmBgYAoKYGBge3J9CiMgZ2V0IHRoZSBmaXJzdCAxMCB0ZXJtcyBvZiBlYWNoIHRvcGljCnRlcm0gPC0gdGVybXMobGRhLCAxMCkKCnRlcm0KYGBgCgpXZSBjYW4gdmlzdWFsaXplIHRoZSBmaXJzdCAxMCB0ZXJtcyBvZiBlYWNoIHRvcGljLgpgYGB7cn0KIyBjb25zdHJ1Y3QgYSB0aWR5IGRhdGEgZnJhbWUgb2YgdGhlIExEQSBtb2RlbCByZXN1bHQgYnkgdGlkeSBmdW5jdGlvbiBmcm9tIHRpZHl0ZXh0IHBhY2thZ2UKdG9waWNzIDwtIHRpZHkobGRhLCBtYXRyaXggPSAiYmV0YSIpCgojIGdlbmVyYXRlIHRoZSB0b3AgMTAgdGVybXMgb2YgZWFjaCB0b3BpYwp0b3BfdGVybXMgPC0gdG9waWNzICU+JQogIGdyb3VwX2J5KHRvcGljKSAlPiUKICB0b3BfbigxMCwgYmV0YSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UodG9waWMsIC1iZXRhKQoKIyBwbG90IHRoZSBmaXJzdCAxMCB0ZXJtcyBieSBiZXRhIHZhbHVlCnRvcF90ZXJtcyAlPiUKICBtdXRhdGUodGVybSA9IHJlb3JkZXJfd2l0aGluKHRlcm0sIGJldGEsIHRvcGljKSkgJT4lCiAgZ2dwbG90KGFlcyh0ZXJtLCBiZXRhLCBmaWxsID0gZmFjdG9yKHRvcGljKSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZ3Bsb3QyOjpsYWJzKAogICAgeCA9IE5VTEwsCiAgICB0aXRsZSA9ICJUb3AgMTAgdGVybXMgaW4gZWFjaCB0b3BpYyIsCiAgICBjYXB0aW9uID0gIlxuU291cmNlOiBEYXRhIGNvbGxlY3RlZCBmcm9tIFR3aXR0ZXIncyBSRVNUIEFQSSB2aWEgcnR3ZWV0IikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeF9yZW9yZGVyZWQoKQpgYGAKVGhlIGJldGEgdmFsdWUgdGVsbHMgdXMgcHJvYmFiaWxpdHkgb2YgdGhhdCB0ZXJtICh3b3JkKSBiZWxvbmdpbmcgdG8gdGhhdCB0b3BpYy4KCldlIGNhbiBzZWUgc29tZSB0ZXJtcyBsaWtlIGBoZWxwYCwgYG5lZWRgLCBgdHJ1bXBgIGFuZCBgcGFuZGVtaWNgIGFwcGVhciBpbiBtb3JlIHRoYW4gMSB0b3BpYywgdGhpcyBpcyBub3Qgc3VycHJpc2VkLiBUaGVyZSBhcmUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGVzZSBjb2xsZWN0aW9ucyBvZiB0ZXJtcy4gCi0gVG9waWMgMSBpcyBhYm91dCBgdGVzdGAsIGBkaWVgIGZyb20gYHBvc2l0aXZlYCwgbGlrZSBhIHRvcGljIGFib3V0IHRoZSBuZXcgY2FzZXMgYW5kIGRlYXRoIHJlcG9ydC4gCi0gVG9waWMgMiBpcyBhYm91dCBgc3RheSBob21lYCwgYG5lZWRgIGFuZCBgaGVscGAsIGxpa2UgYSB0b3BpYyBhYm91dCB3aGF0IHBlb3BsZSBkby4KLSBUb3BpYyAzIGlzIG1vcmUgcmVsYXRlZCB0byBgY2hpbmFgLiBXZSBrbm93IHRoYXQgcmVjZW50bHkgdGhlIHNwcmVhZGluZyBwYWNlIG9mIHRoZSBwYW5kZW1pYyBpbiBDaGluYSBpcyBhbG1vc3Qgc3RvcHBlZC4gCi0gSW4gdG9waWMgNCwgdGhlIHRlcm1zIGB0cnVtcGAsIGBrbm93YCBhbmQgYGdvb2RgIGFwcGVhciB0b2dldGhlci4KCiMgNS4gQ29uY2x1c2lvbgpCeSB1c2luZyBhIGNvbWJpbmF0aW9uIG9mIEJhZyBvZiBXb3JkcyBhbmFseXNpcywgc2VudGltZW50IGFuYWx5c2lzLCBhc3NvY2lhdGlvbiBydWxlcyBhbmQgdG9waWMgbW9kZWxpbmcsIHdlIGhhdmUgY29tZSB0byBhIGdvb2QgdW5kZXJzdGFuZGluZyBvZiB0aGUgbGF0ZXN0IENPVklELTE5IHR3ZWV0cy4KCi0gT3ZlcmFsbCwgdGhlIHR3ZWV0cyBjb252ZXkgYSBzaXR1YXRpb24gdGhhdCBtYW55IHBlb3BsZSBuZWVkIGhlbHAgYXMgdGhlIGhpZ2ggZnJlcXVlbmN5IG9mIHdvcmRzIHN1Y2ggYXMgYG5lZWRgLCBgaGVscGAgYW5kIG1hbnkgdHdlZXRzIG1lbnRpb25lZCB3ZSBzaG91bGQgYHN0YXkgaG9tZWAgdG8gYHByb3RlY3RgIG91cnNlbHZlcyBhcyBpdCBpcyByZWFsbHkgbmVjZXNzYXJ5LgotIEJ5IHNlbnRpbWVudCBhbmFseXNpcywgd2Uga25vdyB0aGF0IHRoZSBDT1ZJRC0xOSBoYXMgbGVhZCB0byBhIGxvdCBvZiBkZWF0aCwgYnV0IG1lZGljYWwgZm9yY2VzIGtlZXAgd29ya2luZywgcGVvcGxlIGhlbHAgYW5kIHN1cHBvcnQgZWFjaCBvdGhlciwgdGhpcyBpcyB2ZXJpZmllZCBhcyBieSBuZXdzIHdlIGtub3cgbWFueSBzdG9yaWVzIGFib3V0IHRoZSBoZWxwIGFuZCBsb3ZlIGluIG91ciBmaWdodCB3aXRoIENPVklELTE5LgoKClBTOiBJbiBhZGRpdGlvbiwgd2UgZmluZCB0aGF0IGB0cnVtcGAgaXMgYSAnc3VycHJpc2UnIHRlcm0gYW5kIHRoZSB0ZXJtcyBgdHJ1bXBgLCBga25vd2AgYW5kIGBnb29kYCBhcmUgaW4gc2FtZSB0b3BpYyBieSBvdXIgbW9kZWxsaW5nLiBJIHRoaW5rIHRoaXMgaXMgYSBnb29kIG1vZGVsbGluZywgYXMgJ2FjdHVhbGx5JyBubyBib2R5IGtub3dzIGNvcm9uYXZpcnVzIGJldHRlciB0aGFuIGhlIGRvZXMsIGp1c3QgbGlrZSBpbiBiZWxvdyB2aWRlby4gOikKCmBgYHtyfQpsaWJyYXJ5KCJodG1sdG9vbHMiKQpsaWJyYXJ5KCJ2ZW1iZWRyIikKYGBgCgpgYGB7cn0KZW1iZWRfdXJsKCJodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PXNSM2Y5NUJHSWlBIikKYGBgCg==